package com.senlingspace.payment.douyin;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.senlingspace.common.exception.payment.DouyinException;
import com.senlingspace.payment.douyin.bean.request.DouyinRequest;
import com.senlingspace.payment.douyin.bean.request.DouyinTokenRequest;
import com.senlingspace.payment.douyin.bean.response.DouyinTokenResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpEntity;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.InputStreamEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.util.EntityUtils;
import org.springframework.http.HttpMethod;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.Objects;

/**
 * 调用抖音 API client
 *
 * @author lebronjames
 * @date 2024/07/22
 */
@Slf4j
public class DouyinHttpClient implements AutoCloseable {

    private static CloseableHttpClient httpClient;

    /**
     * 连接超时时间
     */
    private static Integer connectTimeout = 60000;

    /**
     * 建立连接后超时时间
     */
    private static Integer readTimeout = 100000;

    /**
     * 读取数据超时时间
     */
    private static Integer socketTimeout = 60000;

    /**
     * 服务地址
     */
    private static String serverUrl;

    /**
     * 存储 accessToken
     */
    private static String accessToken;

    /**
     * Client key 和 secret
     */
    private static String clientKey;
    private static String clientSecret;


    public static boolean init(String serverUrl, String clientKey, String clientSecret, Integer connectTimeout, Integer readTimeout, Integer socketTimeout) throws DouyinException {
        if (StringUtils.isBlank(serverUrl)) {
            log.error("serverUrl is null");
            throw new DouyinException("serverUrl is null");
        } else {
            DouyinHttpClient.serverUrl = serverUrl;
        }

        if (StringUtils.isBlank(clientKey)) {
            log.error("clientKey is null");
            throw new DouyinException("clientKey is null");
        }

        if (StringUtils.isBlank(clientSecret)) {
            log.error("clientSecret is null");
            throw new DouyinException("clientSecret is null");
        }

        if (Objects.nonNull(connectTimeout)) {
            DouyinHttpClient.connectTimeout = connectTimeout;
        }

        if (Objects.nonNull(readTimeout)) {
            DouyinHttpClient.readTimeout = readTimeout;
        }

        if (Objects.nonNull(socketTimeout)) {
            DouyinHttpClient.socketTimeout = socketTimeout;
        }

        DouyinHttpClient.clientKey = clientKey;
        DouyinHttpClient.clientSecret = clientSecret;

        log.info("Build HttpClient...");
        httpClient = HttpClientBuilder.create().build();

        log.info("Verify douyin configuration parameters。");
        refreshToken();

        log.info("DouyinHttpClient init successfully...");
        return true;
    }

    private static void refreshToken() {
        DouyinTokenRequest request = new DouyinTokenRequest();
        request.setClientKey(clientKey);
        request.setClientSecret(clientSecret);
        try {
            DouyinTokenResponse verify = invoke(request);
            if (verify.getMessage().equalsIgnoreCase("success")) {
                log.info("验证抖音配置参数成功！");
                DouyinHttpClient.accessToken = verify.getData().getAccessToken(); // 存储 accessToken
            } else {
                log.error("抖音参数配置错误！");
                throw new DouyinException("抖音参数配置错误！");
            }
        } catch (Exception e) {
            log.error("抖音参数配置错误！");
            throw new DouyinException("抖音参数配置错误！");
        }
    }

    public static <T> T invoke(DouyinRequest<T> request) {
        DouyinFunctionEnum function = request.getFunction();
        int retryCount = 0;
        int maxRetries = 1;

        while (retryCount <= maxRetries) {
            try {
                String json;
                if (function.getMethod().equalsIgnoreCase(HttpMethod.POST.name())) {
                    json = post(serverUrl + function.getUrl(), request.getBody());
                } else {
                    json = get(serverUrl + function.getUrl() + "?" + request.getBody());
                }

                JSONObject jsonObject = JSON.parseObject(json);

                if (isTokenExpired(jsonObject)) {
                    log.info("Access token expired, refreshing token...");
                    refreshToken();
                    retryCount++;
                } else {
                    return JSON.parseObject(json, request.getResponseType());
                }
            } catch (Exception e) {
                log.error("Exception during invoke: ", e);
                throw new DouyinException(e.getMessage() != null ? e.getMessage() : (e.getCause() != null ? e.getCause().getMessage() : ""));
            }
        }
        throw new DouyinException("Max retries reached for refreshing access token");
    }

    public static String post(String fullUrl, String body) throws DouyinException {
        if (httpClient == null) {
            throw new DouyinException("HttpClient has not been initialized");
        }
        try {
            log.info("Sending POST request to URL: {}", fullUrl);
            HttpPost httpPost = new HttpPost(fullUrl);
            RequestConfig requestConfig = RequestConfig.custom()
                    .setConnectTimeout(connectTimeout)
                    .setConnectionRequestTimeout(readTimeout)
                    .setSocketTimeout(connectTimeout)
                    .build();
            InputStream stream = new ByteArrayInputStream(body.getBytes(StandardCharsets.UTF_8));
            InputStreamEntity reqEntity = new InputStreamEntity(stream);
            reqEntity.setContentType("application/json");
            httpPost.setEntity(reqEntity);
            httpPost.addHeader("Content-Type", "application/json");
            if (StringUtils.isNotBlank(accessToken)) {
                httpPost.addHeader("access-token", accessToken);
            }
            httpPost.setConfig(requestConfig);

            log.info("Executing request...");
            CloseableHttpResponse response = httpClient.execute(httpPost);
            if (response == null) {
                log.error("response is null.");
                throw new DouyinException("response is null");
            }
            HttpEntity entity = response.getEntity();
            String string = EntityUtils.toString(entity, StandardCharsets.UTF_8);
            log.info("POST接口 --->>> {}， 入参 --->>> {}，抖音响应 --->>> {}", fullUrl, body, string);
            response.close();
            return string;
        } catch (Exception e) {
            log.error("异常 --> url:{},err -->", fullUrl, e);
            throw new DouyinException(e.getMessage() != null ? e.getMessage() : (e.getCause() != null ? e.getCause().getMessage() : ""));
        }
    }

    public static String get(String fullUrl) throws DouyinException {
        if (httpClient == null) {
            throw new DouyinException("HttpClient has not been initialized");
        }
        try {
            log.info("Sending GET request to URL: {}", fullUrl);
            HttpGet httpGet = new HttpGet(fullUrl);
            RequestConfig requestConfig = RequestConfig.custom()
                    .setConnectTimeout(connectTimeout)
                    .setConnectionRequestTimeout(readTimeout)
                    .setSocketTimeout(socketTimeout)
                    .build();
            httpGet.setConfig(requestConfig);
            httpGet.addHeader("Content-Type", "application/json");
            if (StringUtils.isNotBlank(accessToken)) {
                httpGet.addHeader("access-token", accessToken);
            }

            CloseableHttpResponse response = httpClient.execute(httpGet);
            if (response == null) {
                log.error("response is null.");
                throw new DouyinException("response is null");
            }
            HttpEntity entity = response.getEntity();
            String result = EntityUtils.toString(entity, StandardCharsets.UTF_8);
            log.info("GET接口 --->>> {}，抖音响应 --->>> {}", fullUrl, result);
            response.close();
            return result;
        } catch (Exception e) {
            log.error("异常 --> url:{}, err -->", fullUrl, e);
            throw new DouyinException(e.getMessage() != null ? e.getMessage() : (e.getCause() != null ? e.getCause().getMessage() : ""));
        }
    }

    /**
     * 10010: token过期
     * 2190002: access_token无效
     */
    private static boolean isTokenExpired(JSONObject jsonObject) {
        if (jsonObject.containsKey("data") && jsonObject.getJSONObject("data").containsKey("error_code") &&
                (
                        jsonObject.getJSONObject("data").getIntValue("error_code") == 10010 ||
                        jsonObject.getJSONObject("data").getIntValue("error_code") == 2190002 ||
                                jsonObject.getJSONObject("data").getIntValue("error_code") == 2190008
                )) {
            return true;
        }
        if (jsonObject.containsKey("extra") && jsonObject.getJSONObject("extra").containsKey("error_code") &&
                (jsonObject.getJSONObject("extra").getIntValue("error_code") == 10010 || jsonObject.getJSONObject("extra").getIntValue("error_code") == 2190002)) {
            return true;
        }
        return false;
    }


    @Override
    public void close() throws Exception {
        if (httpClient != null) {
            log.info("http client close...");
            httpClient.close();
        }
    }




}
